﻿using System;
using System.Collections.Generic;
using UnityEngine;
using Net.Helper;
using Unity;
using Cysharp.Threading.Tasks;

namespace GameDesigner
{
    /// <summary>
    /// 状态机 v2017/12/6 -> 2024.9.21(大改) -> 2025.2.28
    /// </summary>
    [Serializable]
    public class StateMachine : IStateMachine
    {
        [SerializeField] private string _name;
        public string name { get => _name; set => _name = value; }
        [SerializeField] private RefIdentity _id;
        public RefIdentity Id { get => _id ??= new(); set => _id = value; }
        public IStateController Controller { get; set; }
        public IStateLayer Layer { get; set; }
        public StateMachineView View => Controller.View;
        public Transform transform => Controller.transform;
        /// <summary>
        /// 默认状态ID
        /// </summary>
        public int defaulId;
        /// <summary>
        /// 当前运行的状态索引
        /// </summary>
		public int stateId;
        /// <summary>
        /// 切换的状态id
        /// </summary>
        internal int nextId;
        /// <summary>
        /// 切换下一个状态的动作索引
        /// </summary>
        internal int nextActionId;
        /// <summary>
        /// 切换下一个状态时, 如果是子状态机, 则可以进入子状态的指定状态id
        /// </summary>
        internal int nextChildId;
        /// <summary>
        /// 所有状态
        /// </summary>
#if UNITY_2020_1_OR_NEWER
        [NonReorderable]
#endif
        [SerializeField] private State[] states;
#if UNITY_EDITOR
        [SerializeField] private List<int> selectStates;
#endif
        /// <summary>
        /// 以状态ID取出状态对象
        /// </summary>
        /// <param name="stateID"></param>
        /// <returns></returns>
        public State this[int stateID] => states[stateID];
        /// <summary>
        /// 获取 或 设置 默认状态
        /// </summary>
        public State DefaultState
        {
            get
            {
                if (defaulId < states.Length)
                    return states[defaulId];
                return null;
            }
            set { defaulId = value.Id; }
        }
        /// <summary>
        /// 当前状态
        /// </summary>
		public State CurrState => states[stateId];
#if UNITY_EDITOR
        /// <summary>
        /// 选择的状态
        /// </summary>
        public State SelectState
        {
            get
            {
                selectStates ??= new List<int>();
                if (selectStates.Count <= 0)
                    return null;
                if (selectStates[0] >= states.Length)
                {
                    selectStates.Clear();
                    return null;
                }
                return states[selectStates[0]];
            }
            set
            {
                if (!selectStates.Contains(value.Id))
                    selectStates.Add(value.Id);
            }
        }
#endif
        public State[] States { get => states ??= new State[0]; set => states = value; }

        public int NextId { get => nextId; set => nextId = value; }
#if UNITY_EDITOR
        public List<int> SelectStates { get => selectStates ??= new List<int>(); set => selectStates = value; }
#endif
        public int StateId { get => stateId; set => stateId = value; }
        public IAnimationHandler Handler { get; set; }
        public IStateMachine Parent { get; set; }
        public StateMachineUpdateMode UpdateMode { get; set; }

        public IStateMachine Root
        {
            get
            {
                IStateMachine root = this;
                while (root.Parent != null && root.Parent != this)
                    root = root.Parent;
                return root;
            }
        }
        [SerializeField] private StateMachine[] subStateMachines;
        public IStateMachine[] SubStateMachines
        {
            get => subStateMachines ??= new StateMachine[0];
            set
            {
                subStateMachines = new StateMachine[value.Length];
                for (int i = 0; i < value.Length; i++)
                    subStateMachines[i] = (StateMachine)value[i];
            }
        }

        private bool isInitialize;

        public State AddState(string name, params StateBehaviour[] behaviours)
        {
            var state = new State(this)
            {
                name = name,
#if UNITY_EDITOR
                position = new Rect(5000, 5000, 150, 30)
#endif
            };
            state.AddComponent(behaviours);
            return state;
        }

        public State AddState(string name, bool actionSystem, bool animLoop, bool isCrossFade, bool isExitState, int dstStateID, params StateBehaviour[] behaviours)
        {
            var state = AddState(name, behaviours);
            state.actionSystem = actionSystem;
            state.animLoop = animLoop;
            state.isCrossFade = isCrossFade;
            state.isExitState = isExitState;
            state.dstStateID = dstStateID;
            return state;
        }

        public State AddSubState(string stateName, Vector2 position)
        {
            var state = new State(this)
            {
                name = stateName,
#if UNITY_EDITOR
                position = new Rect(position, new Vector2(150, 30)),
#endif
                Type = StateType.SubStateMachine,
            };
            Root.AddSubStateMachine(state, new StateMachine() { name = stateName });
            return state;
        }

        public void AddSubStateMachine(State state, IStateMachine stateMachine)
        {
            state.SubId = SubStateMachines.Length;
            SubStateMachines = ArrayHelper.Add(SubStateMachines, stateMachine);
            UpdateSubStateMachineIds();
            UpdateRefId(state);
        }

        public void UpdateSubStateMachineIds()
        {
            for (int i = 0; i < SubStateMachines.Length; i++)
            {
                SubStateMachines[i].Id.Value = i;
            }
        }

        public void UpdateRefId(State state)
        {
            state.SubId = SubStateMachines[state.SubId].Id;
            SubStateMachines[state.SubId].name = state.name;
        }

        public State AddParentState(string stateName, Vector2 position)
        {
            var state = new State(this)
            {
                name = stateName,
#if UNITY_EDITOR
                position = new Rect(position, new Vector2(150, 30)),
#endif
                Type = StateType.Parent,
            };
            return state;
        }

        public State CloneState(State state)
        {
            var newState = CloneHelper.DeepCopy<State>(state, new List<Type>() { typeof(UnityEngine.Object), typeof(StateMachine) });
#if UNITY_EDITOR
            newState.preId = newState.Id;
#endif
            newState.Id = States.Length;
            newState.stateMachine = this;
            if (newState.Type == StateType.SubStateMachine)
            {
                var stateMachine = CloneHelper.DeepCopy<StateMachine>(state.SubStateMachine, new List<Type>() { typeof(UnityEngine.Object), typeof(StateMachine) });
                stateMachine.Parent = state.SubStateMachine.Parent;
                Root.AddSubStateMachine(newState, stateMachine);
            }
            States = ArrayHelper.Add(States, newState);
            return newState;
        }

        public void RemoveState(int id)
        {
            RemoveStates(new State[] { states[id] });
        }

        public void RemoveStates(State[] states)
        {
            for (int i = 0; i < states.Length; i++)
            {
                var state = states[i];
                if (state.Type == StateType.SubStateMachine)
                {
                    state.SubStateMachine.RemoveStates(state.SubStateMachine.States); //必须是移除自己的状态
                    Root.RemoveSubStateMachine(state);
                }
                state.Id.Value = -1; //由于顺序问题, 可能选择要删除的状态是2,5,3这种就会导致索引溢出问题
            }
            UpdateStates();
        }

        public void RemoveSubStateMachine(State state)
        {
            SubStateMachines = ArrayHelper.RemoveAt(Root.SubStateMachines, state.SubId);
            UpdateSubStateMachineIds();
        }

        public void ClearStates()
        {
            states = new State[0];
        }

        public void UpdateStates()
        {
            for (int i = states.Length - 1; i >= 0; i--)
            {
                if (states[i].Id == -1)
                {
                    ArrayHelper.RemoveAt(ref states, i);
                    continue;
                }
                states[i].Id.Value = i;
                for (int j = states[i].transitions.Length - 1; j >= 0; j--)
                {
                    if (states[i].transitions[j].NextId == -1)
                    {
                        ArrayHelper.RemoveAt(ref states[i].transitions, j);
                    }
                }
            }
        }

        public void Init(params object[] args)
        {
            if (isInitialize)
                return;
            isInitialize = true;
            if (States.Length == 0)
                return;
            Handler.OnInitializeBegin(this);
            foreach (var state in States)
                state.Init(this, args);
            Handler.OnInitializeEnd(this);
            if (Parent == null)
                DefaultState.Enter(-1, 0, true);
            stateId = nextId = defaulId;
        }

        public void Execute(StateMachineUpdateMode currMode)
        {
            if (!isInitialize)
                return;
            if (states.Length == 0)
                return;
            if (stateId != nextId)
            {
                if (currMode != StateMachineUpdateMode.Update) //不允许LateUpdate和FixedUpdate更新方式执行切换状态
                    return;
                var currIdTemo = stateId;
                var nextIdTemp = nextId; //防止进入或退出行为又执行了EnterNextState切换了状态
                stateId = nextId;
                states[currIdTemo].Exit();
                states[nextIdTemp].Enter(nextChildId, nextActionId, true);
                return; //有时候你调用Play时，并没有直接更新动画时间，而是下一帧才会更新动画时间，如果Play后直接执行下面的Update计算动画时间会导致鬼畜现象的问题
            }
            if (stateId >= states.Length)
            {
                Debug.LogError($"{transform}没有状态ID:{stateId}");
                return;
            }
            CurrState.Update(currMode);
        }

        /// <summary>
        /// 当进入下一个状态, 你也可以立即进入当前播放的状态, 如果不想进入当前播放的状态, 使用StatusEntry方法
        /// </summary>
        /// <param name="nextStateIndex">下一个状态的ID</param>
		public void EnterNextState(int nextStateIndex, int actionId = 0) => ChangeState(nextStateIndex, actionId, true);

        /// <summary>
        /// 进入下一个状态, 如果状态正在播放就不做任何处理, 如果想让动作立即播放可以使用 OnEnterNextState 方法
        /// </summary>
        /// <param name="stateID"></param>
        public void StatusEntry(int stateID, int actionId = 0) => ChangeState(stateID, actionId, false);

        /// <summary>
        /// 切换状态
        /// </summary>
        /// <param name="stateId"></param>
        /// <param name="force"></param>
        public void ChangeState(int stateId, int actionId = 0, bool force = false)
        {
            if (force)
            {
                states[this.stateId].Exit();
                states[stateId].Enter(-1, actionId, true);
                nextId = this.stateId = stateId;
                nextActionId = actionId;
                nextChildId = -1;
            }
            else if (nextId != stateId)
            {
                nextId = stateId;
                nextActionId = actionId;
                nextChildId = -1;
            }
        }

        /// <summary>
        /// 切换子状态，不需要退出当前状态
        /// </summary>
        /// <param name="stateId"></param>
        /// <param name="actionId"></param>
        public void ChangeChildState(int stateId, int actionId = 0)
        {
            states[stateId].Enter(-1, actionId, true);
            nextId = this.stateId = stateId;
            nextActionId = actionId;
            nextChildId = -1;
        }

        /// <summary>
        /// 切换状态, 如果状态是子状态机, 则可以指定进入子状态机的某个状态
        /// </summary>
        /// <param name="stateId">当前状态机id</param>
        /// <param name="childStateId">子状态机的状态id</param>
        /// <param name="actionId">子状态机的状态动作id</param>
        /// <param name="force">强制进入?</param>
        public void ChangeState(int stateId, int childStateId, int actionId = 0, bool force = false)
        {
            if (force)
            {
                states[this.stateId].Exit();
                states[stateId].Enter(childStateId, actionId, true);
                nextId = this.stateId = stateId;
                nextActionId = actionId;
                nextChildId = childStateId;
            }
            else if (nextId != stateId)
            {
                nextId = stateId;
                nextActionId = actionId;
                nextChildId = childStateId;
            }
        }

        public void OnDestroy()
        {
            foreach (var state in states)
            {
                foreach (var behaviour in state.behaviours)
                    behaviour.OnDestroy();
                foreach (var transition in state.transitions)
                    foreach (var behaviour in transition.behaviours)
                        behaviour.OnDestroy();
                foreach (var action in state.actions)
                    foreach (var behaviour in action.behaviours)
                        behaviour.OnDestroy();
            }
            foreach (var stateMachine in subStateMachines)
                stateMachine.OnDestroy();
        }

#if UNITY_EDITOR
        public void OnScriptReload(StateMachineView view)
        {
            var isDirty = false;
            for (int i = 0; i < States.Length; i++)
            {
                var state = States[i];
                state.Id.Value = i;
                state.stateMachine = this;
                if (state.Type == StateType.SubStateMachine)
                {
#if STATE_MACHINE_OLD
                    if (state.subStateMachine != null && !string.IsNullOrEmpty(state.subStateMachine.name)) //如果是旧状态, 肯定是给起名了的
                    {
                        Root.AddSubStateMachine(state, state.subStateMachine);
                        state.subStateMachine = default;
                        isDirty = true;
                    }
#endif
                    Root.UpdateRefId(state);
                    state.SubStateMachine.Controller = view.controller;
                    state.SubStateMachine.Layer = state.stateMachine.Layer;
                    state.SubStateMachine.Parent = this;
                    state.SubStateMachine.OnScriptReload(view);
                    //continue; //子状态机不需要往下执行 (推翻)--如果这个子状态机的状态在父状态机上进行连线, 则会出问题, 所以不能continue
                }
                if (state.Type == StateType.Parent)
                    continue; //父状态不需要往下执行
                for (int j = 0; j < state.behaviours.Length; j++)
                    ReloadBehaviour<StateBehaviour>(ref state.behaviours, ref j, state.Id, null);
                foreach (var transition in state.transitions)
                {
#if STATE_MACHINE_OLD
                    if (transition.oldNextId != -1)
                    {
                        transition.NextId = transition.oldNextId;
                        transition.oldNextId = -1;
                        isDirty = true;
                    }
#endif
                    transition.stateMachine = this;
                    transition.Id = state.Id;
                    transition.NextId = transition.NextState.Id;
                    for (int j = 0; j < transition.behaviours.Length; j++)
                    {
                        ReloadBehaviour<TransitionBehaviour>(ref transition.behaviours, ref j, state.Id, transitionBehaviour => transitionBehaviour.Transition = transition);
                    }
                }
                foreach (var action in state.actions)
                {
                    action.stateMachine = this;
                    action.Id = state.Id;
                    if (action.length <= 0f)
                    {
                        action.length = view.GetAnimClipLength(action);
                        if (action.length <= 0f)
                            action.length = 1f;
                        isDirty = true;
                    }
                    for (int j = 0; j < action.behaviours.Length; j++)
                    {
                        ReloadBehaviour<ActionBehaviour>(ref action.behaviours, ref j, state.Id, actionBehaviour => actionBehaviour.Action = action);
                    }
                }
            }
            if (isDirty)
            {
                async void Delay()
                {
                    await UniTask.DelayFrame(10);
                    if (view == null)
                        return;
                    UnityEditor.EditorUtility.SetDirty(view);
                }
                Delay();
            }
        }
        private void ReloadBehaviour<T>(ref BehaviourBase[] behaviours, ref int i, RefIdentity id, Action<T> action) where T : BehaviourBase
        {
            var type = AssemblyHelper.GetType(behaviours[i].name);
            if (type == null)
            {
                ArrayHelper.RemoveAt(ref behaviours, i);
                if (i >= 0) i--;
                return;
            }
            //如果类型相等，说明已经初始化过了，默认是BehaviourBase类型
            //如果在运行中再次初始化会把正常游戏的初始化丢弃导致报错
            if (behaviours[i].GetType() == type && Application.isPlaying)
                return;
            var metadatas = new List<Metadata>(behaviours[i].Metadatas);
            var active = behaviours[i].Active;
            var show = behaviours[i].show;
            behaviours[i] = (BehaviourBase)Activator.CreateInstance(type);
            behaviours[i].Reload(type, metadatas);
            behaviours[i].Id = id;
            behaviours[i].Active = active;
            behaviours[i].show = show;
            behaviours[i].stateMachine = this;
            action?.Invoke((T)behaviours[i]);
        }
#endif
    }
}